﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;

namespace BTKERP.Common.DataHelper
{
    public class EntityHelper
    {
        private static readonly Dictionary<string, TypeCache> cache = new Dictionary<string, TypeCache>();
        internal class TypeCache
        {
            public Type Type { get; set; }

            public PropertyInfo[] PropertyInfos { get; set; }
        }

        public static List<T> EncapsulateObj<T>(DataTable dt) where T : new()
        {
            List<T> result = new List<T>();
            List<PropertyInfo> propertyList = GetPropertyInfoList<T>(dt.Columns);

            foreach (DataRow row in dt.Rows)
            {
                result.Add(SetPropertyValue<T>(row, propertyList));
            }

            return result;
        }

        private static List<PropertyInfo> GetPropertyInfoList<T>(DataColumnCollection columns) where T : new()
        {
            List<PropertyInfo> propertyList = new List<PropertyInfo>();
            if (columns != null)
            {
                Type type = typeof(T);
                PropertyInfo[] properties = null;
                if (cache.ContainsKey(type.FullName))
                {
                    properties = cache[type.FullName].PropertyInfos;
                }
                else
                {
                    properties = type.GetProperties((BindingFlags.Public | BindingFlags.Instance));
                    cache[type.FullName] = new TypeCache()
                    {
                        Type = type,
                        PropertyInfos = properties
                    };
                }

                foreach (var item in properties)
                {
                    if (columns.Contains(item.Name))
                    {
                        propertyList.Add(item);
                    }
                }

            }

            return propertyList;
        }

        private static T SetPropertyValue<T>(DataRow dr, List<PropertyInfo> propertyList) where T : new()
        {
            T t = new T();
            foreach (var pro in propertyList)
            {
                if (dr[pro.Name] == DBNull.Value)
                {
                    if (pro.PropertyType.IsClass || pro.PropertyType.FullName.Contains("System.Nullable"))
                    {
                        pro.SetValue(t, null, null);
                    }
                    else
                    {
                        pro.SetValue(t, 0, null);
                    }
                }
                else
                {
                    pro.SetValue(t, dr[pro.Name], null);                    
                }
            }
            return t;
        }


        public static T EncapsulateObj<T>(DataRow dr) where T : new()
        {
            List<PropertyInfo> propertyList = GetPropertyInfoList<T>(dr.Table.Columns);
            return SetPropertyValue<T>(dr, propertyList);
        }

        public static T EncapsulateBasicObj<T>(DataRow dr)
        {
            if (dr.Table.Columns.Count == 1)
                return (T)dr[0];
            else
                return default(T);
        }

        public static T SetResponseValue<T>(Dictionary<string, string> returnValue) where T : new()
        {
            T t = new T();

            Type type = typeof(T);
            PropertyInfo[] properties = null;
            if (cache.ContainsKey(type.FullName))
            {
                properties = cache[type.FullName].PropertyInfos;
            }
            else
            {
                properties = type.GetProperties();
                cache[type.FullName] = new TypeCache()
                {
                    Type = type,
                    PropertyInfos = properties
                };
            }

            decimal numValue;
            foreach (var info in properties)
            {
                if (returnValue.ContainsKey(info.Name)) // have return value 
                {
                    if (info.PropertyType.FullName.Contains("System.Nullable")) // decimal
                    {
                        if (decimal.TryParse(returnValue[info.Name], out numValue)) //value is correct
                        {
                            info.SetValue(t, numValue, null);
                        }
                    }
                    else
                    {
                        info.SetValue(t, returnValue[info.Name], null);
                    }
                }
            }
            return t;
        }
    }
}
